package start.util;

import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;
import org.apache.commons.lang3.StringUtils;

import javax.xml.bind.*;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collection;

/**
 * JAXB Java和XML的自由转换方法
 * 编  号：<br/>
 * 名  称：JaxbUtil<br/>
 * 描  述：<br/>
 * 完成日期：2016年7月6日 下午11:09:21<br/>
 * 编码作者：宁文武
 */
public class JaxbUtil {
    private JAXBContext jaxbContext;  
  
    /** 
     * @param types  所有需要序列化的Root对象的类型. 
     */  
    public JaxbUtil(Class<?>... types) {  
        try {  
            jaxbContext = JAXBContext.newInstance(types);  
        } catch (JAXBException e) {  
            throw new RuntimeException(e);  
        }  
    }  
  
    /** 
     * Java Object->XML. 
     */  
    public String toXml(Object root, String encoding) {  
        try {  
            StringWriter writer = new StringWriter();  
            createMarshaller(encoding).marshal(root, writer);  
            return writer.toString();  
        } catch (JAXBException e) {  
            throw new RuntimeException(e);  
        }  
    }

    public String toXmlEscape(Object root, String encoding) {
        try {
            StringWriter writer = new StringWriter();
            Marshaller marshaller = createMarshallerEscape(encoding);
            marshaller.marshal(root, writer);
            return writer.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    /*public String toXmlNoStandalone(Object root, String encoding) {
        try {
            StringWriter writer = new StringWriter();
            Marshaller marshaller = createMarshaller(encoding);
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
            writer.append("<?xml version=\"1.0\" encoding=\""+ encoding +"\"?>\n");
            marshaller.marshal(root, writer);
            return writer.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    public String toXmlEscapeStandalone(Object root, String encoding) {
        try {
            StringWriter writer = new StringWriter();
            Marshaller marshaller = createMarshallerEscape(encoding);
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
            writer.append("<?xml version=\"1.0\" encoding=\""+ encoding +"\"?>\n");
            marshaller.marshal(root, writer);
            return writer.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }*/
  
    /** 
     * Java Object->XML, 特别支持对Root Element是Collection的情形. 
     */  
    @SuppressWarnings("rawtypes")
	public String toXml(Collection root, String rootName, String encoding) {  
        try {  
            CollectionWrapper wrapper = new CollectionWrapper();  
            wrapper.collection = root;  
  
            JAXBElement<CollectionWrapper> wrapperElement = new JAXBElement<CollectionWrapper>(  
                    new QName(rootName), CollectionWrapper.class, wrapper);  
  
            StringWriter writer = new StringWriter();  
            createMarshaller(encoding).marshal(wrapperElement, writer);  
  
            return writer.toString();  
        } catch (JAXBException e) {  
            throw new RuntimeException(e);  
        }  
    }  
  
    /** 
     * XML->Java Object. 
     */  
    @SuppressWarnings("unchecked")  
    public <T> T fromXml(String xml) {  
        try {  
            StringReader reader = new StringReader(xml);  
            return (T) createUnmarshaller().unmarshal(reader);  
        } catch (JAXBException e) {  
            throw new RuntimeException(e);  
        }  
    }  
  
    /** 
     * XML->Java Object, 支持大小写敏感或不敏感. 
     */  
    @SuppressWarnings("unchecked")  
    public <T> T fromXml(String xml, boolean caseSensitive) {  
        try {  
            String fromXml = xml;  
            if (!caseSensitive)  
                fromXml = xml.toLowerCase();  
            StringReader reader = new StringReader(fromXml);  
            return (T) createUnmarshaller().unmarshal(reader);  
        } catch (JAXBException e) {  
            throw new RuntimeException(e);  
        }  
    }  
  
    /** 
     * 创建MARSHALLER,设定encoding 
     */  
    public Marshaller createMarshaller(String encoding) {  
        try {  
            Marshaller marshaller = jaxbContext.createMarshaller();

            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            if (StringUtils.isNotBlank(encoding)) {
                marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);  
            }  
            return marshaller;  
        } catch (JAXBException e) {  
            throw new RuntimeException(e);  
        }  
    }

    public Marshaller createMarshallerEscape(String encoding) {
        try {
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.setProperty(CharacterEscapeHandler.class.getName(), new CharacterEscapeHandler() {
                public void escape(char[] ch, int start,int length, boolean isAttVal, Writer writer) throws IOException {
                    writer.write(ch, start, length);
                }
            });

            if (StringUtils.isNotBlank(encoding)) {
                marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
            }
            return marshaller;
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }


  
    /** 
     * 创建UnMarshaller. 
     */  
    public Unmarshaller createUnmarshaller() {  
        try {  
            return jaxbContext.createUnmarshaller();  
        } catch (JAXBException e) {  
            throw new RuntimeException(e);  
        }  
    }  
  
    /** 
     * 封装Root Element是 Collection的情况. 
     */  
    public static class CollectionWrapper {  
        @SuppressWarnings("rawtypes")
		@XmlAnyElement  
        protected Collection collection;  
    }  
}